﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Galaktika.BI.Silverlight.Intermediate.Charting;

namespace Galaktika.BI.Silverlight.Controls.PivotChart
{

    internal class MultipleDictionary<TKey, TValue>
    {
        protected BinaryTree<TKey, TValue> BinaryTree { get; set; }
        protected MultipleDictionary()
        {
        }

        /// <summary>
        /// Initializes a new instance of the MultipleDictionary class.
        /// </summary>
        /// <param name="allowDuplicateValues">The parameter is not used.</param>
        /// <param name="keyEqualityComparer">The parameter is not used.</param>
        /// <param name="valueEqualityComparer">The parameter is not used.</param>
        public MultipleDictionary(bool allowDuplicateValues, IEqualityComparer<TKey> keyEqualityComparer, IEqualityComparer<TValue> valueEqualityComparer)
        {
             BinaryTree = new BinaryTree<TKey, TValue>(
                (left, right) => keyEqualityComparer.GetHashCode(left).CompareTo(keyEqualityComparer.GetHashCode(right)),
                (left, right) => valueEqualityComparer.GetHashCode(left).CompareTo(valueEqualityComparer.GetHashCode(right)));
        }

        /// <summary>
        /// Adds a key/value pair to the dictionary.
        /// </summary>
        /// <param name="key">Key to add.</param>
        /// <param name="value">Value to add.</param>
        public void Add(TKey key, TValue value)
        {
            BinaryTree.Add(key, value);
        }

        /// <summary>
        /// Removes a key/value pair from the dictionary.
        /// </summary>
        /// <param name="key">Key to remove.</param>
        /// <param name="value">Value to remove.</param>
        /// <returns>True if the value was present and removed.</returns>
        public bool Remove(TKey key, TValue value)
        {
            return BinaryTree.Remove(key, value);
        }

        /// <summary>
        /// Gets the count of values in the dictionary.
        /// </summary>
        public int Count
        {
            get
            {
                return BinaryTree.Count;
            }
        }

        /// <summary>
        /// Returns the collection of values corresponding to a key.
        /// </summary>
        /// <param name="key">Specified key.</param>
        /// <returns>Collection of values.</returns>
        public ICollection<TValue> this[TKey key]
        {
            get
            {
                return BinaryTree.GetValuesForKey(key).ToList();
            }
        }

        /// <summary>
        /// Clears the items in the dictionary.
        /// </summary>
        public void Clear()
        {
            BinaryTree.Clear();
        }
    }

    public class LineSeriesEx : LineAreaBaseSeries<LineDataPoint>
    {
        private OrderedMultipleDictionary<IComparable, DataPoint> DataPointsByIndependentValue = null;

        public PointCollection Points
        {
            get { return GetValue(PointsProperty) as PointCollection; }
            private set { SetValue(PointsProperty, value); }
        }

        /// <summary>
        /// Identifies the Points dependency property.
        /// </summary>
        public static readonly DependencyProperty PointsProperty =
            DependencyProperty.Register(
                "Points",
                typeof(PointCollection),
                typeof(LineSeriesEx),
                null);

        public LineSeriesEx()
        {
           DataPointsByIndependentValue = new OrderedMultipleDictionary<IComparable, DataPoint>(false,
                (left, right) =>
                        left.CompareTo(right),
                    (leftDataPoint, rightDataPoint) =>
                            RuntimeHelpers.GetHashCode(leftDataPoint).CompareTo(RuntimeHelpers.GetHashCode(rightDataPoint)));
        }

        protected override void UpdateShape()
        {
            double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value.Value;

            Func<DataPoint, Point> createPoint =
                dataPoint =>
                    new Point(
                        ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value.Value,
                        maximum - ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value.Value);

            IEnumerable<Point> points = null;
            if (ValueHelper.CanGraph(maximum))
            {
                if (ActualIndependentAxis is IRangeAxis)
                {
                    points = DataPointsByIndependentValue.Select(createPoint);
                }
                else
                {
                    points =
                        ActiveDataPoints
                            .Select(createPoint)
                            .OrderBy(point => point.X);
                }

                if (!points.IsEmpty())
                {
                    this.Points = new PointCollection();
                    foreach (Point point in points)
                    {
                        this.Points.Add(point);
                    }
                    return;
                }
            }
            this.Points = null;
        }

        protected override void GetAxes(DataPoint firstDataPoint)
        {
            GetAxes(
               firstDataPoint,
               (axis) => axis.Orientation == AxisOrientation.X,
               () =>
               {
                   IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue);
                   if (axis == null)
                   {
                       axis = new CategoryAxis();
                   }
                   (axis as CategoryAxis).Location = AxisLocation.Left;
                   axis.Orientation = AxisOrientation.Y;
                   return axis;
               },
               (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis,
               () =>
               {
                   DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue);
                   if (axis == null)
                   {
                       throw new InvalidOperationException(Localization.Exc_SeriesNotSupported);
                   }
                   axis.ShowGridLines = true;
                   axis.Location = AxisLocation.Bottom;
                   axis.Orientation = AxisOrientation.X;
                   return axis;
               });
        }

        protected override void UpdateDataPoint(DataPoint dataPoint)
        {
            double maximum = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value.Value;
            if (ValueHelper.CanGraph(maximum))
            {
                double x = ActualIndependentAxis.GetPlotAreaCoordinate(dataPoint.ActualIndependentValue).Value.Value;
                double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(dataPoint.ActualDependentValue).Value.Value;

                if (ValueHelper.CanGraph(x) && ValueHelper.CanGraph(y))
                {
                    double coordinateY = Math.Round(maximum - (y + (dataPoint.ActualHeight / 2)));
                    Canvas.SetTop(dataPoint, coordinateY);
                    double coordinateX = Math.Round(x - (dataPoint.ActualWidth / 2));
                    Canvas.SetLeft(dataPoint, coordinateX);
                }
            }

            if (!UpdatingDataPoints)
            {
                UpdateShape();
            }
        }
    }
}
